There is overlap with problem set 3 from before the first midterm.
You should do these problems on paper or mentally first, and only THEN check the solution.
This document is review of the notion of scope and local variables in Python and Java, and an explanation of how these relate to (and are explained by!) let expression and lambda expressions.
Definition: A local variable is one defined inside a function, a class, or a statement block; its scope is the part of the program where it may be referred to without error.
Consider the following short Java program, which simply prints out the number 15:
public class ScopeTest { public static void main(String args[]) { int x = 5; System.out.println( x * 2 + x ); } }
4.1. What is the rule for the scope of x? Draw a vertical line showing the scope of x.
4.2. Ditto for x and y in:
public class ScopeTest { public static void main(String args[]) { int y = 2; int x = 3 * y; System.out.println(x + y); } }
4.3. Ditto for x,y, and z in:
public class ScopeTest { public static void main(String args[]) { int x = 3; int y = x - 2; int z = 5 * y; System.out.println( x * y + z ); } }
Local variables also occur in function definitions and statement blocks (statements inside curly braces, perhaps in a loop or conditional). Consider the following:
public class ScopeTest { public static void main(String args[]) { int y = 2; if(y >= 0) { int x = 3 * y; System.out.println(x + y); } System.out.println("bye!"); } }
4.4. Show the scope of the variables x and y in this last example.
4.5. Finally, local variables may be created as parameters of functions; show the scope of x, y, and z in this example (remembering that the rule for scope of members of a class is the whole class):
public class ScopeTest { public static int f(int y) { System.out.println("hi"); if(y >= 0) { int z = 5 * y; return ( x * y + z ); } System.out.println("there!"); return y; } public static int x = 3; public static void main(String args[]) { System.out.println( f( x - 2 ) ); System.out.println("bye!"); } }
Now let us see how this all works in Python... Consider the following Python program (either typed into the interpreter, run from a file, or run from a code cell in a notebook):
x = 5 print( x * 2 + x )
4.6. What is the rule for the scope of x defined at the top level (i.e., not in a function)? Draw a vertical line showing the scope of x.
4.7. Ditto for x and y in:
y = 2 x = 3 * y print(x + y)
4.8. Ditto for x,y, and z in:
x = 3 y = x - 2 z = 5 * y print( x * y + z )
Python also has function definitions.
4.9. Show the scope of the variables x and y in this example:
y = 2 def f(x): print(x + y) f(3 * y)
4.10. In Python (but not in Java) functions can be defined locally inside other functions; show the scope of x, y, and z in this example:
x = 3 def f(y): # this is a function g local to f def g(z): return x * y + z return g(5*y) print( f(3) )
When local variables have the same name in different scopes, we can get a "hole in scope" phenomenon, where the outer definition is hidden by an inner definition, because you when you look for a variable definition, you look outwards from the use to the CLOSEST ENCLOSING DEFINITION.
4.11. Show the scope of the three DIFFERENT variables called x in the following Java program:
public class ScopeTest { public static int x = 3; public static int f(int x) { return x + 1; } public static int g(int z) { int x = z + 1; return x; } public static void main(String args[]) { System.out.println( f( 2 * x ) ); System.out.println( g( 2 * x ) ); } }
4.12. Show the scope of the two DIFFERENT variables called x in the following Python program:
x = 3 def f(x): return x + 1 print( f( 2 * x ) )
Lambda expressions were introduced using Haskell syntax.
Lambda calculus: bound and free variables, simple one-step reduction.
Let us say that a variable x
occurs as a bound variable in a lambda expression (\x -> expr)
, where
"\x ->" is the binding for the variable x, and
the scope of the binding is expr
; and a variable y
in a lambda expression
which does not occur in the scope of any bound variable is called a free variable.
For the following lambda expressions, circle each free variable, and for each bound variable, draw a line from the occurrence of the variable in the body to the corresponding lambda binding.
4.13. (\y -> (\x -> y x)) y
4.14. (\x -> x y) (\y -> y x)
4.15. (\x -> (\x -> (\y -> x y)) (\z -> (y z) x))
Problems 4.16 -- 4.18 are optional for midterm 2.
4.16. Draw the abstract syntax tree for the lambda expression in problem 4.13.
4.17. Ditto for 4.14
4.18. Ditto for 4.15
For the following lambda expressions, perform a single step of reduction and show the substitution that was used. Choose the left-most possible reduction.
4.19. (\x -> (\y -> y x)) z
4.20. (\x -> x) ((\x -> x) ((\x -> x) z))
4.21. The lambda expression in problem 4.14.
4.22. (\x -> x (y x)) (\y -> y z)
4.23. (\x -> x (\x -> y x)) z
4.24. (\x -> ((x (\x -> y x)) x) x) y
4.25. (\x -> (x (\y -> y x)) x) z
In Haskell you can set up local variables using a let
expression. In the next series of problems, we explore this idea and show its correspondence with a certain kind of lambda expression (showing that lambda expressions really are the "assembly language of functional programming").
Recall that the let expression with a single binding has the following syntax:
let <var> = <expression> in <body>
The rule for the scope of the local variable is simple: the scope is the body.
The body can be any expression or even another let expression; here are some examples:let x = 5 in x * 2 + x let y = 2 in let x = 3 * y in x + y let x = 3 in let y = x - 2 in let z = 5 * y in x * y + z
The nesting in the last one is clearer if we add parentheses:
(let x = 2 in (let y = x - 2 in (let z = 9 in x * y + z)) ) --------------------------------------------- body of (let x = ... ) ------------------------ body of (let y = ... ) --------- body of (let z = ... )
For the following, first show the scope of the bound variables in the expression, and then calculate its value, doing all beta-reductions first and arithmetic operations last. Show the substitution and how itis applied to the expression.
4.26.
let x = 5 in x * 2 + x,
4.27.
let x = 8 in x - (3 * x),
4.28.
let x = (3 * 2) in (x-1) * x,
4.29.
let y = 2 in let x = 3 * y in x + y
4.30.
let x = 3 in let y = x - 2 in let z = 5 * y in x * y + z
4.31. Now redo the last one, but evaluate any arithmetic expressions as soon as possible:
Now we investigate how let expressions are really "syntactic sugar" for lambda expressions.
4.32. Find a lambda expression which can be reduced in one step and then evaluated to do the exact same thing as
let x = 5 in x * 2 + xShow the lambda expression and then show how it is evaluated.
4.33. Ditto for:
let y = 2 in let x = 3 * y in x * yevaluating any arithmetic expressions as soon as possible.
4.34. Ditto for let x = 3 in let y = x - 2 in let z = 5 * y in x * y + z.
4.35. Ditto for the following let expression:
let x = 3 in let x = 2 * x in x + 1
4.36. Show the scope of the variable y in the let expression in 4.31 and also in its corresponding lambda expression.
4.37. Ditto for the two occurrences of x in the let and lambda expressions in 4.35, showing the "hole in scope" for the outer occurrence.
4.38. Show the correspondence between the Java program in Problem 4.2 and the let expression in Problem 6.14.
4.39. Ditto for the Python program in Problem 4.8.
For each of these expressions, state whether upon evaluation it
4.40.
let x = z * 2 z = 2 w = x `div` z y = z + w in y * 10
4.41.
let x = w * 2 z = 7 w = z `div` x y = x + w in y * 10
4.42.
let x = z * 2 y = z w = let v = x + 1 u = y - z in v * u v = x + w z = 2 in v * 10
4.43.
let n = 2 m = n + let a = 4 b = a * n in a - k k = a * 3 in n + m + k
4.44.
let (_,(x,y)) = ((2,3),(4,z)) (z:zs) = [1,2,3,4] (a:b:_) = [x+y,x*y,x-y] in (head zs) + a + b
4.45.
let n = 2 x = 3 y = x + (let n = 3 in x * n) in let n = n + 1 in n + y
4.46.
let n = 2 t = 1:[x*2 | x <- t] f n = 1:[x*n| x <- (f n)] in take (n+2) (f (head (tail t)))